from functools import wraps
import time
import json
import base64

from django.http import HttpRequest
from rest_framework.request import Request
from rest_framework import exceptions

from system.services import encrypt_service
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding


def generate_openapi_token(certificate_data):
    padder = padding.PKCS7(128).padder()
    auth_data = {
        'created_at': int(time.time())
    }
    auth_json = json.dumps(auth_data, separators=(',', ':')).encode('utf-8')
    padded_text = padder.update(auth_json) + padder.finalize()
    ciphertext = Cipher(
        algorithms.AES(base64.b64decode(certificate_data['second_key'])),
        modes.CBC(base64.b64decode(certificate_data['holder_open_id'])),
        backend=default_backend()
    ).encryptor().update(padded_text)
    token = base64.b64encode(ciphertext).decode('utf-8')
    return token


def generate_openapi_headers(certificate_data):
    token = generate_openapi_token(certificate_data)
    headers = {
        'token': token,
        'open-id': certificate_data['holder_open_id']
    }
    return headers


def verify_openapi_token(token, open_id):
    from connection.services import certificate_service

    certificate = certificate_service.get_certificate_by_open_id(open_id, is_voided=False)
    if not certificate:
        raise exceptions.NotAuthenticated()
    private_key_pem, _, _ = encrypt_service.get_encrypt_key()
    if not private_key_pem:
        raise exceptions.NotAuthenticated()
    open_id = base64.b64decode(open_id)
    token = base64.b64decode(token)
    second_key = base64.b64decode(certificate.second_key)
    auth_json = Cipher(
        algorithms.AES(second_key),
        modes.CBC(open_id),
        backend=default_backend()
    ).decryptor().update(token)
    unpadder = padding.PKCS7(128).unpadder()
    auth_json = unpadder.update(auth_json) + unpadder.finalize()
    auth_data = json.loads(auth_json)
    created_at = auth_data.get('created_at')
    now = int(time.time())
    if created_at > now or now - created_at > 60 * 3:
        raise exceptions.NotAuthenticated()
    return certificate


def openapi_auth():
    def decorator(view_func):
        @wraps(view_func)
        def _wrapped_view(obj, *args, **kwargs):
            request = obj
            if not isinstance(obj, HttpRequest) and not isinstance(obj, Request):
                request = args[0]
            token = request.META.get('HTTP_TOKEN', '').strip()
            open_id = request.META.get('HTTP_OPEN_ID', '').strip()
            if not token or not open_id:
                raise exceptions.NotAuthenticated()
            certificate = verify_openapi_token(token, open_id)
            connection = certificate.connection
            request.certificate_data = certificate.data
            request.connection = connection
            return view_func(obj, *args, **kwargs)
        return _wrapped_view
    return decorator
